#include <os/walltime.h>
#include <os/clock.h>
#include <os/safety.h>
#include <os/debug.h>
#include <arch/interrupt.h>

walltime_t walltime;
const char month_day[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static uint32_t month[] = {
    0,
    31 * DAY,
    (31 + 29) * DAY,
    (31 + 29 + 31) * DAY,
    (31 + 29 + 31 + 30) * DAY,
    (31 + 29 + 31 + 30 + 31) * DAY,
    (31 + 29 + 31 + 30 + 31 + 30) * DAY,
    (31 + 29 + 31 + 30 + 31 + 30 + 31) * DAY,
    (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31) * DAY,
    (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30) * DAY,
    (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31) * DAY,
    (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30) * DAY,
    (31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31) * DAY};

char *weekday[7] = {
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday"};

static int WallTimeGetMonthDay()
{
    // if is month 2 ,specific deal with
    if (walltime.month == 2)
        return month_day[walltime.month] + WallTimeIsLeapYear(walltime.month) ? 1 : 0;
    else
        return month_day[walltime.month];
}

static int WallTimeGetYearDay()
{
    int i;
    int sum = 0;

    // sum all month to current month days
    for (i = 1; i < walltime.month; i++)
    {
        // month 2 maybe is 28 0r 29 days
        if (i == 2)
            sum += month_day[i] + WallTimeIsLeapYear(walltime.year);
        else
            sum += month_day[i];
    }

    // add this month days
    sum += walltime.day;

    if (walltime.month >= 2)
    {
        if (walltime.month == 2)
        {
            if (walltime.day == 28)
            {
                sum += WallTimeIsLeapYear(walltime.year);
            }
        }
        else
        {
            sum += WallTimeIsLeapYear(walltime.year);
        }
    }
    return sum;
}

static int WalltimeGetWeekDay(int year, int month, int day)
{
    int c, y, week;

    // month 1 and month 2 are count to last year month 13,14
    if (month < 3)
    {
        year--;
        month += 12;
    }
    c = year / 100;
    y = year - c * 100;
    week = (c / 4) - 2 * c + y + (y / 4) + (13 * (month + 1) / 5) + day - 1;
    // deal week<0 sutiation
    if (week < 0)
        week = week + 7 % 7;
    else
        week = week % 7;

    return week;
}

int WallTimeIsLeapYear(int year)
{
    // year%400=0
    if (!(year % 400))
        return 1;
    else
        // year%4=0 and year%100!=0
        if (!(year % 4) && (year % 100) != 0)
            return 1;
    return 0;
}

void WallTimeUpdateSecond()
{
    walltime.second++;
    if (walltime.second > 59)
    {
        walltime.minute++;
        walltime.second = 0;
        if (walltime.minute > 59)
        {
            walltime.hour++;
            walltime.minute = 0;
            if (walltime.hour > 23)
            {
                walltime.day++;
                walltime.hour = 0;
                walltime.week_days++;
                if (walltime.week_days > 6)
                {
                    walltime.week_days = 0;
                }
                // days of year
                walltime.year_days = WallTimeGetYearDay();
                // month
                if (walltime.day > WallTimeGetMonthDay())
                {
                    walltime.month++;
                }
                walltime.day = 1;
                if (walltime.month > 12)
                {
                    walltime.year++;
                    walltime.month = 1;
                }
            }
        }
    }
}

int SysGetWallTime(walltime_t *wt)
{
    return MemCopyToUser(wt, &walltime, sizeof(walltime_t));
}

uint64_t WallTimeMakeTimeStamp(walltime_t *wt)
{
    uint64_t res;
    uint32_t year;

    year = wt->year - 70;
    res = YEAR * year + DAY * (year + 1) / 4;
    res += month[wt->month];
    if (wt->month > 1 && ((year + 2) % 4))
    {
        res -= DAY;
    }
    res += DAY * (wt->day - 1);
    res += HOUR * (wt->hour);
    res += MINUTE * (wt->minute);
    res += wt->second;

    return res;
}

void WallTimePrintf()
{
    KPrint("time:%d:%d:%d date:%d/%d/%d\n",
           (uint32_t)walltime.hour, (uint32_t)walltime.minute, (uint32_t)walltime.second,
           (uint32_t)walltime.year, (uint32_t)walltime.month, (uint32_t)walltime.day);
    KPrint("week day:%d %s year day:%d\n", (uint32_t)walltime.week_days, weekday[walltime.week_days], (uint32_t)walltime.year_days);
}

void WallTimeInit()
{
    DisInterrupt();
    walltime.year = TimeGetYear();
    walltime.month = TimeGetMonth();
    walltime.day = TimeGetDay();
    walltime.hour = TimeGetHour();
    walltime.minute = TimeGetMinute();
    walltime.second = TimeGetSecond();
    EnInterrupt();

// translation to local time
#ifdef CONFIG_TIMEZONE_AUTO
    if (walltime.hour >= 16)
        walltime.hour -= 16;
    else
        walltime.hour += 8;
#endif

    walltime.week_days = WalltimeGetWeekDay(walltime.year, walltime.month, walltime.day);
    walltime.year_days = WallTimeGetYearDay();

    // printf walltime to screen
    WallTimePrintf();
}
